指令选择从LLVM IR中创建机器指令，由MachineInstr类表示，但这并不是结束。MachineInstr类的实例仍然携带额外的信息，比如标签或标志。为了通过机器码组件发出一条指令，需要将MachineInstr的实例降低为MCInst的实例。机器代码组件，提供了将指令写入目标文件或将其作为汇编文本输出的功能。M88kAsmPrinter类负责发出整个编译单元，向下转译指令委托给M88kMCInstLower类。

汇编输出器是后端中运行的最后一个通道，其实现存储在M88kAsmPrinter.cpp文件中:

\begin{enumerate}
\item
M88kAsmPrinter类的声明位于匿名命名空间中。除了构造函数，只重写getPassName()函数和emitInstruction()函数，getPassName()函数以人类可读的字符串形式返回通道的名称:

\begin{cpp}
namespace {
class M88kAsmPrinter : public AsmPrinter {
public:
    explicit M88kAsmPrinter(
        TargetMachine &TM,
        std::unique_ptr<MCStreamer> Streamer)
        : AsmPrinter(TM, std::move(Streamer)) {}

    StringRef getPassName() const override {
        return "M88k Assembly Printer";
    }

    void emitInstruction(const MachineInstr *MI) override;
};
} // end of anonymous namespace
\end{cpp}

\item
像许多其他类一样，必须在目标注册表中注册汇编输出器:

\begin{cpp}
extern "C" LLVM_EXTERNAL_VISIBILITY void
LLVMInitializeM88kAsmPrinter() {
    RegisterAsmPrinter<M88kAsmPrinter> X(
        getTheM88kTarget());
}
\end{cpp}

\item
emitInstruction()方法负责将机器指令(MI)发送到输出流，将指令的向下转译委托给M88kMCInstLower类:

\begin{cpp}
void M88kAsmPrinter::emitInstruction(
        const MachineInstr *MI) {
    MCInst LoweredMI;
    M88kMCInstLower Lower(MF->getContext(), *this);
    Lower.lower(MI, LoweredMI);
    EmitToStreamer(*OutStreamer, LoweredMI);
}
\end{cpp}
\end{enumerate}

基类AsmPrinter提供了许多可以覆盖的有用钩子，emitStartOfAsmFile()方法在事件触发之前调用，而emitEndOfAsmFile()在所有事件触发之后调用，这些方法可以生成有针对性的数据或代码文件的开始和结束。类似地，emitFunctionBodyStart()和emitFunctionBodyEnd()方法也会在函数体发出前后调用。请阅读llvm/include/llvm/CodeGen/AsmPrinter.h文件中的注释，了解可以自定义哪些内容。

M88kMCInstLower类向下转译操作数和指令，实现包含两个用于此目的的方法。声明在M88kMCInstLower.h文件中:

\begin{cpp}
class LLVM_LIBRARY_VISIBILITY M88kMCInstLower {
public:
    void lower(const MachineInstr *MI, MCInst &OutMI) const;
    MCOperand lowerOperand(const MachineOperand &MO) const;
};
\end{cpp}

该定义在M88kMCInstLower.cpp文件中:

\begin{enumerate}
\item
为了将MachineOperand降格为MCOperand，需要检查操作数类型。这里，只通过提供原始的MachineOperand值来创建mcoperand等效寄存器和立即值，从而处理寄存器和立即值。当表达式作为操作数引入，这个方法就需要增强了:

\begin{cpp}
MCOperand M88kMCInstLower::lowerOperand(
        const MachineOperand &MO) const {
    switch (MO.getType()) {
    case MachineOperand::MO_Register:
        return MCOperand::createReg(MO.getReg());

    case MachineOperand::MO_Immediate:
        return MCOperand::createImm(MO.getImm());

    default:
        llvm_unreachable("Operand type not handled");
    }
}
\end{cpp}

\item
指令的向下转译也是类似的。首先，复制操作码，然后处理操作数。MachineInstr的实例可以添加隐式操作数，这些操作数不会向下转译，需要过滤:

\begin{cpp}
void M88kMCInstLower::lower(const MachineInstr *MI,
MCInst &OutMI) const {
    OutMI.setOpcode(MI->getOpcode());
    for (auto &MO : MI->operands()) {
        if (!MO.isReg() || !MO.isImplicit())
            OutMI.addOperand(lowerOperand(MO));
    }
}
\end{cpp}
\end{enumerate}

这样，就实现了汇编输出器。现在，需要把所有的碎片拼凑起来。我们将在下一节中进行此操作。



















































